package cz.via.facepalm.calendar;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import cz.via.facepalm.shared.CActivity;
import cz.via.facepalm.shared.CUser;
import cz.via.facepalm.shared.CUserActivity;
import cz.via.facepalm.shared.ServerOperations;

public class ServerComunicator {

	private static DefaultHttpClient httpClient = new DefaultHttpClient();
	private static String serverURL = "http://via-calendar.appspot.com/calendar_server/json";
//	private static String serverURL = "http://192.168.33.125:8888/calendar_server/json";
	private static Gson gson = new Gson();
	
	public static void listUsersForActivity(Long activityId,final DoneCallback<List<CUserActivity>> done){
		executeRequest(ServerOperations.LoadUsersAttendanceForActivity,new DoneCallback<String>() {
			@Override					
			public void done(String result) {
				Type type = new TypeToken<Collection<CUserActivity>>(){}.getType();
				Collection<CUserActivity> fromJson = gson.fromJson(result,type);
				done.done(new ArrayList<CUserActivity>(fromJson));
			}
		},new KeyValuePair("cactivity", activityId+""));
	}
	
	public static void saveOrChangeAttendance(CUserActivity userAct,final DoneCallback<Void> done){
		executeRequest(ServerOperations.SaveOrChangeAttendance,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				done.done(null);
			}
		},new KeyValuePair(ServerOperations.ARG_PARAM,gson.toJson(userAct)));
	}
	
	public static void listUserActivities(Long userId,final DoneCallback<List<CUserActivity>> done){
		executeRequest(ServerOperations.LoadUserActivities,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				Type type = new TypeToken<Collection<CUserActivity>>(){}.getType();
				Collection<CUserActivity> fromJson = gson.fromJson(result,type);
				done.done(new ArrayList<CUserActivity>(fromJson));
			}
		},new KeyValuePair("cuser", userId+""));
	}

	public static void unSubscribeToActivity(long userId,long activityId,final DoneCallback<Void> done){
		executeRequest(ServerOperations.UnsubscribeToActivity,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				done.done(null);
			}
		},new KeyValuePair("cuser", userId+""),new KeyValuePair("cactivity", activityId+""));
	}
	
	public static void subscribeToActivity(long userId,long activityId,final DoneCallback<Void> done){
		executeRequest(ServerOperations.SubscribeToActivity,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				done.done(null);
			}
		},new KeyValuePair("cuser", userId+""),new KeyValuePair("cactivity", activityId+""));
	}
	
	public static void listActivities(final DoneCallback<List<CActivity>> done){
		executeRequest(ServerOperations.ListActivities,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				Type type = new TypeToken<Collection<CActivity>>(){}.getType();
				Collection<CActivity> fromJson = gson.fromJson(result,type);
				done.done(new ArrayList<CActivity>(fromJson));
			}
		});
	}
	
	public static void createActivity(CActivity activity,final DoneCallback<Void> done){
		executeRequest(ServerOperations.CreateActivity,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				done.done(null);
			}
		},new KeyValuePair(ServerOperations.ARG_PARAM,gson.toJson(activity)));
	}
	
	public static void createUser(CUser user,final DoneCallback<Void> done){
		executeRequest(ServerOperations.CreateUser,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				done.done(null);
			}
		},new KeyValuePair(ServerOperations.ARG_PARAM, gson.toJson(user)));
	}
	
	public static void loadUser(String name,String pass,final DoneCallback<CUser> done){
		executeRequest(ServerOperations.LoadUser,new DoneCallback<String>() {
			@Override
			public void done(String result) {
				done.done(gson.fromJson(result, CUser.class));
			}
		},new KeyValuePair("name", name),new KeyValuePair("pass", pass));
		
	}
	
	private static boolean gtfo = false;
	static{
		Thread thread = new Thread(){
			@Override
			public void run() {
				while(!gtfo){
						try {
							QI poll;
							poll = que.take();
							if(poll!=null){
								executeAsznc(poll.req, poll.done);
							}
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						
				}
			}
		};
		thread.start();
	}
	
	private static class QI{
		String req;
		DoneCallback<String> done;
		public QI(String req, DoneCallback<String> done) {
			super();
			this.req = req;
			this.done = done;
		}
	}
	
	private static BlockingQueue<QI> que = new LinkedBlockingQueue<ServerComunicator.QI>();
	
	public static class KeyValuePair{
		public String key;
		public String value;
		public KeyValuePair(String key, String value) {
			super();
			this.key = key;
			this.value = value;
		}
	}
	
	private static void executeRequest(ServerOperations op,DoneCallback<String> res,KeyValuePair...params) {
		List<KeyValuePair> pairs = new ArrayList<ServerComunicator.KeyValuePair>();
		pairs.add(new KeyValuePair(ServerOperations.OPERATION_PARAM,op.name()));
		pairs.addAll(Arrays.asList(params));
		StringBuilder req = new StringBuilder(serverURL);
		req.append("?");
		for (KeyValuePair keyValuePair : pairs) {
			req.append(keyValuePair.key).append("=").append(URLEncoder.encode(keyValuePair.value)).append("&");
		}
		que.offer(new QI(req.toString(),res));
	}

	
	
	private static void executeAsznc(String req,DoneCallback<String> res) {
		HttpGet httpGet = new HttpGet(req);
		
		try {
			HttpResponse execute = httpClient.execute(httpGet);
			InputStream is = execute.getEntity().getContent();
			BufferedReader reader = new BufferedReader(new InputStreamReader(is));
			StringBuilder sb = new StringBuilder();
			String readline ;
			while((readline = reader.readLine())!=null){
				sb.append(readline+"\n");
			}
			res.done(sb.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
}
